#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

/*
 * return rect's size 0 indicated (r1 r2 not joined) else (joined)
 */
static Rect& measureRects(const Rect &r1, const Rect &r2, Rect &join);
template <typename T>
static inline bool vector_contains(const vector<T> &v, const T &data) {
    for(size_t i = 0; i < v.size(); i++) {
        if(data == v[i]) {
            return true;
        }
    }
    return false;
}

#define SET_RECT_VIA_2P(rect, p1, p2) do{\
                (rect).x = min((p1).x, (p2).x);\
                (rect).y = min((p1).y, (p2).y);\
                (rect).width = abs((p2).x - (p1).x);\
                (rect).height= abs((p2).y - (p1).y); } while(0)

static void onMouse(int event, int x, int y, int flags, void *parm)
{
    /*
	cout << dec << "event = " << event << ", (" << x << ", " << y <<
	    "), flags = " << flags << hex << ", parm = 0x" << parm << endl;
    */

	Mat *pImg = (Mat *) parm;

	static Scalar scalar(255, 255, 255);
    static Scalar scalar_zero(0);
	static int oldEvent, newEvent, oldFlag, newFlag;
    static Point startPoint, currentPoint;
    static Rect currentRect;
    static vector<Rect> vRects;

	newEvent = event;
	newFlag = flags;
    currentPoint.x = x;
    currentPoint.y = y;

    switch(newEvent) {
        case  CV_EVENT_MOUSEMOVE: {
            if(newFlag == CV_EVENT_FLAG_LBUTTON) {
                pImg -> setTo(scalar_zero);

                SET_RECT_VIA_2P(currentRect, startPoint, currentPoint);
                /*
                cout << dec << "currentRect : (" << currentRect.x << ", " << currentRect.y 
                    << ") with " << currentRect.width << " x " << currentRect.height << endl;
                */
                rectangle(*pImg, currentRect, scalar);

                for(size_t i = 0; i < vRects.size(); i ++) {
                    rectangle(*pImg, vRects[i], scalar);
                }

            }
            break;
        }

        case  CV_EVENT_LBUTTONDOWN: {
            if(oldFlag == 0) {
                startPoint.x = x;
                startPoint.y = y;
            }
            break;
        }

        case  CV_EVENT_LBUTTONUP: {
            if(oldEvent == CV_EVENT_MOUSEMOVE && oldFlag == CV_EVENT_FLAG_LBUTTON) {
                vRects.push_back(currentRect);
                for(size_t i = 0; i < vRects.size(); i ++) {
                    cout << dec << "vRects[" << i << "]: (" << vRects[i].x << ", " << vRects[i].y 
                        << ") with " << vRects[i].width << " x " << vRects[i].height << endl;
                }

                //check duplicate ++++
                static vector<int>  vDeletes;
                vDeletes.clear();
                for(size_t i = 0; i < vRects.size(); i ++) {
                    for (size_t j = i + 1; j < vRects.size(); j ++) {

                        cout << i << " <---> " << j<< endl;
                        static Rect joinRect;
                        static Scalar invalidColor(0, 0, 255);
                        size_t deleted;
                        measureRects(vRects[i], vRects[j], joinRect);
                        if(joinRect.width*joinRect.height > 0) {
                            deleted = 
                                (vRects[i].width * vRects[i].height > vRects[j].width * vRects[j].height) ?
                                j : i;

                            rectangle(*pImg, vRects[deleted], invalidColor);
                            if(!vector_contains(vDeletes, (int) deleted)) {
                                vDeletes.push_back(deleted);
                                cout << "deteled " << deleted << endl;
                            }
                        }
                    }
                }
                for(size_t delete_count = 0; delete_count < vDeletes.size(); delete_count ++) {
                    vRects.erase(vRects.begin() + vDeletes[delete_count]);
                }
                //check duplicate ----

            }
                                  
            break;
        }

        case CV_EVENT_RBUTTONUP: {
            vRects.clear();
            pImg -> setTo(scalar_zero);
            break;
        }
    
        default:
            break;
    
    }

	oldEvent = event;
	oldFlag = flags;
}

int main()
{
	string strWindowName_0("main_window");
	namedWindow(strWindowName_0);
	Mat origImg(Size(800, 600), CV_8UC3, Scalar(0));
	setMouseCallback(strWindowName_0, onMouse, (void *)&origImg);

	while (waitKey(30) != 27) {
		imshow(strWindowName_0, origImg);
	}

	return 0;
}

#define rectCmp(r1, r2) ((r1).width*(r1).height - (r2).width*(r2).height)
static Rect& measureRects(const Rect &r1, const Rect &r2, Rect &joinRect) {
    Point centerR1(r1.x + r1.width/2, r1.y + r1.height/2); 
    Point centerR2(r2.x + r2.width/2, r2.y + r2.height/2);

    if((abs(centerR1.x - centerR2.x) < r1.width/2 + r2.width/2)
            && (abs(centerR1.y - centerR2.y) < r1.height/2 + r2.height/2)) {   //必有重合的部分
        Point joinP1(max(r1.x, r2.x), max(r1.y, r2.y));
        Point joinP2(min(r1.x + r1.width, r2.x + r2.width), min(r1.y + r1.height, r2.y + r2.height));
        SET_RECT_VIA_2P(joinRect, joinP1, joinP2);
    } else {
        joinRect.x = joinRect.y = joinRect.width = joinRect.height = 0;
    }

    return joinRect;
}
